#include <iostream>
#include <memory>
#include <vector>
#include "mem_pool_cpp.h"

namespace Fake {

static void unique_ptr_test(InnoMemPool *in_pool) {
  bool check_flag = true;
  auto custom_deleter = [&](uint8_t *const to_be_free) { Fake::mem_pool_free(in_pool, to_be_free); };

  const std::unique_ptr<uint8_t[], decltype(custom_deleter)> shr_mem_ptr1(Fake::mem_pool_alloc(in_pool),
                                                                          custom_deleter);
  if (shr_mem_ptr1) {
    *shr_mem_ptr1.get() = 100;
  } else {
    check_flag = false;
  }

  if (check_flag) {
    const std::unique_ptr<uint8_t[], decltype(custom_deleter)> shr_mem_ptr2(Fake::mem_pool_alloc(in_pool),
                                                                            custom_deleter);

    if (shr_mem_ptr2) {
      std::vector<uint8_t> vec1{97, static_cast<uint8_t>('b'), 99};
      (void)std::copy(vec1.begin(), vec1.end(), shr_mem_ptr2.get());
      shr_mem_ptr2[3] = 100;
      shr_mem_ptr2[4] = static_cast<uint8_t>('e');
      shr_mem_ptr2[5] = 0;
      std::cout << shr_mem_ptr2.get() << std::endl;
    }
  }
}

static void shared_ptr_test(InnoMemPool *in_pool) {
  bool check_flag = true;
  auto custom_deleter = [&](uint8_t *const to_be_free) { Fake::mem_pool_free(in_pool, to_be_free); };

  uint8_t *const mem_ptr1 = Fake::mem_pool_alloc(in_pool);
  const std::shared_ptr<uint8_t> shr_mem_ptr1(mem_ptr1, custom_deleter);
  if (shr_mem_ptr1) {
    *shr_mem_ptr1 = 100;
  } else {
    check_flag = false;
  }

  if (check_flag) {
    uint8_t *const mem_ptr2 = Fake::mem_pool_alloc(in_pool);
    const std::shared_ptr<uint8_t> shr_mem_ptr2(mem_ptr2, custom_deleter);
    if (shr_mem_ptr2) {
      std::vector<uint8_t> vec1{97, static_cast<uint8_t>('b'), 99, static_cast<uint8_t>('\0')};
      (void)std::copy(vec1.begin(), vec1.end(), shr_mem_ptr2.get());
      // shr_mem_ptr2.get()[3] = 100;  // Noncompliant
      std::string mem_str = reinterpret_cast<const char *>(shr_mem_ptr2.get());
      mem_str[mem_str.length() - 1] = '\0';

      if (static_cast<uint32_t>(mem_str.length()) > MEM_BLOCK_SIZE) {
        std::cerr << "mem_str.length() error" << std::endl;
      }
    }
  }
}

}  // namespace Fake

int32_t main() {
  int32_t main_res{0};  // Initialize with a default value

  try {
    Fake::InnoMemPool in_pool;
    (void)Fake::mem_pool_init(&in_pool);
    std::cout << "mem_pool size should be: " << Fake::INNO_MEM_POOL_SIZE << std::endl;
    std::cout << "mem_pool size is " << sizeof(in_pool.mem_pool) << std::endl;
    if (in_pool.free_block_list != NULL) {
      std::cout << "free_block_list not NULL" << std::endl;
      if (in_pool.free_block_list->next_block != NULL) {
        std::cout << "free_block_list->next_block not NULL" << std::endl;
      }
    }

    (void)Fake::unique_ptr_test(&in_pool);
    (void)Fake::shared_ptr_test(&in_pool);

    main_res = 0;  // Successful execution
  } catch (...) {
    std::cerr << "Exception occurred" << std::endl;
    main_res = 1;  // Exception occurred
  }

  return main_res;  // Single point of exit
}
